home *** CD-ROM | disk | FTP | other *** search
/ Visual Cafe 3 / Visual Cafe 3.ISO / Vcafe / Source.bin / FormattedTextField.java < prev    next >
Text File  |  1998-10-19  |  37KB  |  1,192 lines

  1. package symantec.itools.awt;
  2.  
  3. import java.awt.Toolkit;
  4. import java.awt.Dimension;
  5. import java.awt.Event;
  6. import java.awt.Font;
  7. import java.awt.FontMetrics;
  8. import java.awt.TextField;
  9. import java.awt.event.*;
  10.  
  11. //    05/29/97    RKM    Convert to 1.1 event model
  12. //                    Deprecated getter for booleans, and added is
  13. //                    Used add/removeNotify to listen to events
  14. //                    WARNING: Could not test component due bug in MRJ,
  15. //                    doesn't work InputEvent.consume()
  16. //  05/14/97    TNM If there is not mask it works like java.awt.TextField
  17. //    01/29/97    TWB    Integrated changes from Windows
  18. //  07/30/97    CAR inner adaptor class implements java.io.Serializable
  19. //  08/26/97    CAR changed keyPressed to call processchar with getKeyChar instead of getKeyCode
  20. //  10/09/97    LAB Re-wrote processChar.  This fixes many issues with this component.  Modified
  21. //                    several functions, and added several, in this process.  These modifications
  22. //                    address Mac bug #3383, #7636, #7638 and #8517. !!! LAB !!! Note, processChar
  23. //                    is by no means optimized; this should be done at a later date.  Deprecated
  24. //                    setEditFont and getEditFont.  Removed overriden setFont.
  25. //  10/11/97    LAB Removed unnecessary items related to overriden editable.  Handle empty
  26. //                    string masks correctly now (Addresses Mac Bug #9198).  Fixed problems
  27. //                    between setText and setMask confusing the unformattedCurrent string and
  28. //                    Re-wrote applyMask to correctly update the unformattedCurrent string
  29. //                    (Addresses Mac Bugs #9199, #9168, and #9167).  Moved filtering code
  30. //                    into filterChar to unify applyMask and applyMaskToChar.  Added
  31. //                    getFormattedText to return the contents of the text field, since
  32. //                    getText now returns the unformatted string.
  33. //  18/2/98         fbeninc cheched for \0 sent with shift key.line790/1006
  34. //  10/6/98     MSH Bug fix 65516, fixed shift key moving cursor back one position
  35. /**
  36.  * Creates a box in which the user can type text. Text formatting logic is
  37.  * applied to the user input.
  38.  * <p>
  39.  * Use FormattedTextField to
  40.  * <UL>
  41.  * <DT>╖ Limit the type of text that can be entered in the text box.</DT>
  42.  * <DT>╖ Display text captured from the keyboard.</DT>
  43.  * <DT>╖ Edit a line of text.</DT>
  44.  * <DT>╖ Post an event based on text input from the keyboard.</DT>
  45.  * </UL>
  46.  * If the text box already contains text, the user can select the default text
  47.  * and delete or edit it.
  48.  * <p>
  49.  * @version 1.1, August 30, 1997
  50.  * @author Symantec
  51.  */
  52. public class FormattedTextField extends TextField
  53. {
  54.     /**
  55.      * Character mask constant, the character following this ESCAPE will be placed into input string.
  56.      */
  57.     public final char ESCAPE                = '/';
  58.     /**
  59.      * Character mask constant, input must be a digit character.
  60.      */
  61.     public final char DIGIT                 = '9';
  62.     /**
  63.      * Character mask constant, input must be a sign (+ or -) character.
  64.      */
  65.     public final char SIGN                  = '+';
  66.     /**
  67.      * Character mask constant, input must be a digit or sign (+ or -) character.
  68.      */
  69.     public final char DIGIT_OR_SIGN         = '-';
  70.     /**
  71.      * Character mask constant, input must be an uppercase alpha character.
  72.      */
  73.     public final char ALPHA_UPPER           = 'A';
  74.     /**
  75.      * Character mask constant, input must be a lowercase alpha character.
  76.      */
  77.     public final char ALPHA_LOWER           = 'a';
  78.     /**
  79.      * Character mask constant, input must be an alpha character (it will be converted to uppercase).
  80.      */
  81.     public final char ALPHA_TO_UPPER        = 'U';
  82.     /**
  83.      * Character mask constant, input must be an alpha character (it will be converted to lowercase).
  84.      */
  85.     public final char ALPHA_TO_LOWER        = 'L';
  86.     /**
  87.      * Character mask constant, input must be a digit or an uppercase alpha character.
  88.      */
  89.     public final char ALPHANUMERIC_UPPER    = 'X';
  90.     /**
  91.      * Character mask constant, input must be a digit or a lowercase alpha character.
  92.      */
  93.     public final char ALPHANUMERIC_LOWER    = 'x';
  94.     /**
  95.      * Character mask constant, input must be a digit or alpha character (it will be converted to uppercase).
  96.      */
  97.     public final char ALPHANUMERIC_TO_UPPER = 'N';
  98.     /**
  99.      * Character mask constant, input must be a digit or alpha character (it will be converted to lowercase).
  100.      */
  101.     public final char ALPHANUMERIC_TO_LOWER = 'n';
  102.     /**
  103.      * Character mask constant, input can be any character.
  104.      */
  105.     public final char ANY                   = '*';
  106.  
  107.     /**
  108.      * Constructs a new FormattedTextField. It can have 256 columns.
  109.      */
  110.     public FormattedTextField()
  111.     {
  112.         this(256);
  113.     }
  114.  
  115.     /**
  116.      * Constructs a new FormattedTextField with the specified number of columns.
  117.      * @param i the number of character columns
  118.      */
  119.     public FormattedTextField(int i)
  120.     {
  121.         this("", i);
  122.     }
  123.  
  124.     /**
  125.      * Constructs a new FormattedTextField containing the specified text.
  126.      * It can have 256 columns.
  127.      * @param s the field text
  128.      */
  129.     public FormattedTextField(String s)
  130.     {
  131.         this(s, 256);
  132.     }
  133.  
  134.     /**
  135.      * Constructs a new FormattedTextField containing the specified text and
  136.      * able to have the specified number of columns.
  137.      * @param s the field text
  138.      * @param i the number of character columns
  139.      */
  140.     public FormattedTextField(String s, int i)
  141.     {
  142.         super(s, i);
  143.         caret = 0;
  144.         unformattedCurrent    = "";
  145.         designTimeText        = "";
  146.     }
  147.  
  148.     /**
  149.      * Sets the valid string format mask for this field.
  150.      * @param mask the new valid string format mask
  151.      * @see #getMask
  152.      */
  153.     public void setMask(String mask)
  154.     {
  155.         if(mask != null && mask.length() < 1)
  156.         {
  157.             this.mask = null;
  158.         }
  159.         else
  160.         {
  161.             this.mask = mask;
  162.         }
  163.  
  164.         maskStripEscapes = stripMaskEscapes();
  165.         maskStripEscapesLen = maskStripEscapes.length();
  166.         //Apply the new mask to our current text.
  167.         setText(getText());
  168.     }
  169.  
  170.     /**
  171.      * Gets the current valid string format mask.
  172.      * @return the string that defines valid input into this field.
  173.      * @see #setMask
  174.      */
  175.     public String getMask()
  176.     {
  177.         return mask;
  178.     }
  179.  
  180.     /**
  181.      * @deprecated
  182.      */
  183.     public void setEditFont(Font f)
  184.     {
  185.         setFont(f);
  186.     }
  187.  
  188.     /**
  189.      * @deprecated
  190.      */
  191.     public Font getEditFont()
  192.     {
  193.         return getFont();
  194.     }
  195.  
  196.     /**
  197.      * Sets the text of this TextComponent to the specified text
  198.      * after passing the specified text throught the filter.
  199.      * @param t the new text to be set
  200.      * @see #getText
  201.      */
  202.     public void setText(String t)
  203.     {
  204.         //Keep the original string around at design time to return from getText.
  205.         if(java.beans.Beans.isDesignTime())
  206.             designTimeText = t;
  207.  
  208.         String newString;
  209.  
  210.         if(mask == null)
  211.         {
  212.             newString    = t;
  213.             unformattedCurrent = newString;
  214.         }
  215.         else
  216.         {
  217.             newString = applyMask(t);
  218.         }
  219.         super.setText(newString);
  220.     }
  221.  
  222.     /**
  223.      * Gets the unformatted string of filtered characters.
  224.      * @return the string of unformatted characters that were accepted by
  225.      * the mask filtering process.  At design time this returns the text passed
  226.      * to setText.
  227.      * @see #setText
  228.      * @see #getFormattedText
  229.      */
  230.     public String getText()
  231.     {
  232.         if(java.beans.Beans.isDesignTime())
  233.             return designTimeText;
  234.         else
  235.             return unformattedCurrent;
  236.     }
  237.  
  238.     /**
  239.      * Gets the displayed contents of the component.
  240.      * @return the string currently displayed by the component.
  241.      * @see #setText
  242.      * @see #getText
  243.      */
  244.     public String getFormattedText()
  245.     {
  246.         return super.getText();
  247.     }
  248.  
  249.     /**
  250.      * Sets whether the text field is editable.
  251.      * @param f true if the text field is editable, false otherwise
  252.      * @see java.awt.TextComponent#isEditable
  253.      */
  254.     public void setEditable(boolean f)
  255.     {
  256.         super.setEditable(f);
  257.  
  258.         setEnabled(f);
  259.     }
  260.  
  261.     /**
  262.      * Returns a string defining an input template. The mask ESCAPE
  263.      * characters will be place in the string. All other mask characters will
  264.      * have spaces in place of them. For example, a mask of "99/-9/-9" results
  265.      * in a string set to "  - - ".
  266.      * @return the template string defined by the mask, with spaces as placeholders.
  267.      */
  268.     public String getMaskTemplateString()
  269.     {
  270.         String newString = "";
  271.         if(mask != null)
  272.         {
  273.             int ml = mask.length();
  274.             int maskIndex;
  275.             char maskChar;
  276.  
  277.             for (maskIndex = 0; maskIndex < ml; maskIndex++)
  278.             {
  279.                 maskChar = mask.charAt(maskIndex);
  280.                 newString += maskChar == ESCAPE ? mask.charAt(++maskIndex) : ' ';
  281.             }
  282.         }
  283.         return newString;
  284.     }
  285.  
  286.     /**
  287.      * Clears the contents of the field and sets the text field's contents to
  288.      * be an input template. The mask ESCAPE characters will be place in the
  289.      * field. All other mask characters will have spaces in place of them.
  290.      * For example, a mask of "99/-9/-9" results in the text field getting
  291.      * set to "  - - ".
  292.      */
  293.     public void setFillMask()
  294.     {
  295.         String newString = getMaskTemplateString();
  296.         unformattedCurrent = "";
  297.         super.setText(newString);
  298.     }
  299.  
  300.     /**
  301.      * Tells this component that it has been added to a container.
  302.      * This is a standard Java AWT method which gets called by the AWT when
  303.      * this component is added to a container. Typically, it is used to
  304.      * create this component's peer.
  305.      *
  306.      * It has been overridden here to call addKeyListener.
  307.      *
  308.      * @see #removeNotify
  309.      */
  310.     public synchronized void addNotify()
  311.     {
  312.  
  313.         if (keyListener == null)
  314.         {
  315.             keyListener = new Key();
  316.             addKeyListener(keyListener);
  317.         }
  318.  
  319.         super.addNotify();
  320.     }
  321.  
  322.     /**
  323.      * Tells this component that it is being removed from a container.
  324.      * This is a standard Java AWT method which gets called by the AWT when
  325.      * this component is removed from a container. Typically, it is used to
  326.      * destroy the peers of this component and all its subcomponents.
  327.      *
  328.      * It has been overridden here to call removeKeyListener.
  329.      *
  330.      * @see #addNotify
  331.      */
  332.     public synchronized void removeNotify()
  333.     {
  334.         if (keyListener != null)
  335.         {
  336.             removeKeyListener(keyListener);
  337.             keyListener = null;
  338.         }
  339.  
  340.         super.removeNotify();
  341.     }
  342.  
  343.     /**
  344.      * @deprecated As of JDK version 1.1,
  345.      * replaced by getPreferredSize().
  346.      */
  347.     public Dimension preferredSize() {
  348.         Font f = getFont();
  349.         if (f != null) {
  350.             FontMetrics fm = getFontMetrics(f);
  351.             if (mask != null) {
  352.                 int w = fm.stringWidth("W") * maskStripEscapesLen + 4;
  353.                 int h = fm.getHeight() + 4;
  354.                 return new Dimension(w, h);
  355.             }
  356.             else {
  357.                 return new Dimension(fm.stringWidth("ABCDE") + 4, fm.getHeight() + 4);
  358.             }
  359.         }
  360.         return new Dimension(30, 15);
  361.     }
  362.  
  363.     /**
  364.      * Returns the recommended dimensions to properly display this component.
  365.      * This is a standard Java AWT method which gets called to determine
  366.      * the recommended size of this component.
  367.      *
  368.      * @see #getMinimumSize
  369.      */
  370.     public Dimension getPreferredSize() {
  371.         return preferredSize();
  372.     }
  373.  
  374.     /**
  375.      * @deprecated As of JDK version 1.1,
  376.      * replaced by getMinimumSize().
  377.      */
  378.     public Dimension minimumSize() {
  379.         return preferredSize();
  380.     }
  381.  
  382.     /**
  383.      * Returns the minimum dimensions to properly display this component.
  384.      * This is a standard Java AWT method which gets called to determine
  385.      * the minimum size of this component.
  386.      *
  387.      * @see #getPreferredSize
  388.      */
  389.     public Dimension getMinimumSize() {
  390.         return minimumSize();
  391.     }
  392.  
  393.     /**
  394.      * Processes KEY_PRESS and KEY_ACTION events.
  395.      * This is a standard Java AWT method which gets called by the AWT
  396.      * method handleEvent() in response to receiving a KEY_PRESS or
  397.      * KEY_ACTION event. These events occur when this component has the focus
  398.      * and the user presses a "normal" or an "action" (F1, page up, etc) key.
  399.      *
  400.      * @param evt the Event
  401.      * @param key the key that was pressed
  402.      * @return true if the event was handled
  403.      * @see java.awt.Component#keyUp
  404.      * @see java.awt.Component#handleEvent
  405.      */
  406.  
  407.     class Key extends KeyAdapter implements java.io.Serializable
  408.     {
  409.         public void keyTyped(KeyEvent e)
  410.         {
  411.             e.consume();
  412.                processChar(e.getKeyChar());
  413.         }
  414.  
  415.         /**
  416.          * Invoked when a key has been pressed.
  417.          */
  418.         public void keyPressed(KeyEvent e)
  419.         {
  420.             int key = e.getKeyCode();
  421. //            System.out.println( "KeyAdapter- key: " + key );
  422.  
  423.             switch (key)
  424.             {
  425.                 case KeyEvent.VK_LEFT:
  426.                 {
  427.                     if (--caret < 0)
  428.                         caret = 0;
  429.                     break;
  430.                 }
  431.  
  432.                 case KeyEvent.VK_RIGHT:
  433.                 {
  434.                     if (++caret >= maskStripEscapesLen)
  435.                         caret = maskStripEscapesLen;
  436.                     break;
  437.                 }
  438.  
  439.                 case KeyEvent.VK_HOME:
  440.                 {
  441.                     caret = 0;
  442.                     break;
  443.                 }
  444.  
  445.                 case KeyEvent.VK_END:
  446.                 {
  447.                     caret = maskStripEscapesLen;
  448.                     break;
  449.                 }
  450.  
  451.                 case KeyEvent.VK_ENTER:
  452.                 case KeyEvent.VK_TAB:
  453.                 case KeyEvent.VK_SHIFT:
  454.                 {
  455.                     //Do nothing
  456.                     break;
  457.                 }
  458.  
  459.                 // handle VK_BACK_SPACE with keyTyped
  460.                 case KeyEvent.VK_DELETE:
  461.                 {
  462.                     e.consume();
  463.                     processChar(e.getKeyChar());
  464.                     break;
  465.                 }
  466.  
  467.                 default:
  468.                 {
  469.                     e.consume();
  470.                     break;
  471.                 }
  472.             }
  473.         }
  474.     }
  475.  
  476.     /**
  477.      * Returns the next valid character in the input string.
  478.      * @param s the input string
  479.      * @param start the zero-relative index of the first character to check in
  480.      * the input string
  481.      * @param maskChar the mask character used to determine which input characters
  482.      * are valid
  483.      */
  484.     protected int validChar(String s, int start, char maskChar)
  485.     {
  486.         boolean isReturn;
  487.         int len = s.length();
  488.         for (int i = start; i < len; ++i)
  489.         {
  490.             isReturn = false;
  491.             char c = s.charAt(i);
  492.             switch (maskChar)
  493.             {
  494.                 case DIGIT :
  495.                     if (Character.isDigit(c))
  496.                         isReturn = true;
  497.                     break;
  498.  
  499.                 case SIGN :
  500.                     if (c == '+' || c == '-')
  501.                         isReturn = true;
  502.                     break;
  503.  
  504.                 case DIGIT_OR_SIGN :
  505.                     if (Character.isDigit(c) || c == '+' || c == '-')
  506.                         isReturn = true;
  507.                    break;
  508.  
  509.                 case ALPHA_UPPER :
  510.                     if (Character.isUpperCase(c))
  511.                         isReturn = true;
  512.                     break;
  513.  
  514.                 case ALPHA_LOWER :
  515.                     if (Character.isLowerCase(c))
  516.                         isReturn = true;
  517.                     break;
  518.  
  519.                 case ALPHANUMERIC_UPPER :
  520.                     if (Character.isUpperCase(c) || Character.isDigit(c))
  521.                         isReturn = true;
  522.                     break;
  523.  
  524.                 case ALPHANUMERIC_LOWER :
  525.                     if (Character.isLowerCase(c) || Character.isDigit(c))
  526.                         isReturn = true;
  527.                     break;
  528.  
  529.                 case ALPHA_TO_UPPER :
  530.                     if (Character.isUpperCase(c) || Character.isLowerCase(c))
  531.                         isReturn = true;
  532.                     break;
  533.  
  534.                 case ALPHA_TO_LOWER :
  535.                     if (Character.isUpperCase(c) || Character.isLowerCase(c))
  536.                         isReturn = true;
  537.                     break;
  538.  
  539.                 case ALPHANUMERIC_TO_UPPER :
  540.                     if (Character.isUpperCase(c) || Character.isLowerCase(c) || Character.isDigit(c))
  541.                         isReturn = true;
  542.                     break;
  543.  
  544.                 case ALPHANUMERIC_TO_LOWER :
  545.                     if (Character.isUpperCase(c) || Character.isLowerCase(c) || Character.isDigit(c))
  546.                         isReturn = true;
  547.                     break;
  548.  
  549.                 case ANY :
  550.                     isReturn = true;
  551.                     break;
  552.             }
  553.             if(isReturn)
  554.                 return i;
  555.         }
  556.  
  557.         return -1;
  558.     }
  559.  
  560.     /**
  561.      * Returns a formatted string, given an input string and the current valid
  562.      * string format mask.
  563.      * @param s the input string
  564.      * @return the input string formatted using the current valid string
  565.      * format mask.
  566.      */
  567.     protected String applyMask(String s)
  568.     {
  569.         //If there is no mask, there is no formatting needed.
  570.         if(mask == null)
  571.         {
  572.             unformattedCurrent = s;
  573.             return s;
  574.         }
  575.  
  576.         //Reset the unformatted string
  577.         unformattedCurrent = "";
  578.  
  579.         int maskLength = mask.length();
  580.         int stringLength = s == null ? 0 : s.length();
  581.         char maskChar;
  582.         char stringChar;
  583.         String newString = "";
  584.         boolean isValidChar;
  585.  
  586.         int maskIndex    = 0;
  587.         int stringIndex    = 0;
  588.         while(stringIndex < stringLength && maskIndex < maskLength)
  589.         {
  590.             maskChar = mask.charAt(maskIndex);
  591.  
  592.             //If the maskChar is a escape sequence initiator, add the next character to the display string.
  593.             if(maskChar == ESCAPE)
  594.             {
  595.                 ++maskIndex;
  596.                 if(maskIndex < maskLength)
  597.                     newString += mask.charAt(maskIndex);
  598.                 ++maskIndex;
  599.             }
  600.             else
  601.             {
  602.                 stringChar = s.charAt(stringIndex);
  603.                 isValidChar = validChar("" + stringChar, 0, maskChar) != -1 ? true : false;
  604.  
  605.                 //If the character is valid at the index, as defined by the maskChar...
  606.                 if(isValidChar)
  607.                 {
  608.                     //Apply any transformations needed to the character.
  609.                     stringChar =  filterChar(stringChar, maskChar);
  610.                     //Add the character to the string to display.
  611.                     newString += stringChar;
  612.                     //Add the character to the unformatted string to keep track of characters that
  613.                     //were accepted, and not part of the mask.
  614.                     unformattedCurrent += stringChar;
  615.  
  616.                     ++maskIndex;
  617.                 }
  618.                 ++stringIndex;
  619.             }
  620.         }
  621.         //If the string didn't fill the entire mask, then pad the display string with the mask template
  622.         int newLength = newString.length();
  623.         if(newLength < maskStripEscapesLen)
  624.         {
  625.  
  626.             String maskTemplate = getMaskTemplateString();
  627.  
  628.             newString += maskTemplate.substring(newLength > 0 ? newLength - 1 : 0);
  629.         }
  630.         return newString;
  631.     }
  632.  
  633.     /**
  634.      * Apply the mask at index to the given character.
  635.      * @param char targetChar the character to apply the mask to.
  636.      * @param int index the index in the mask to use on the given character.
  637.      * @return A string with the given character with the mask
  638.      * at the given index applied. An empty string if the character was
  639.      * invalid or if the given index was an escape character index.
  640.      */
  641.     protected String applyMaskToChar(char targetChar, int index)
  642.     {
  643.         if(mask == null)
  644.             return ("" + targetChar);
  645.  
  646.  
  647.         if (isEscape(index))
  648.         {
  649.             return "";
  650.         }
  651.         else
  652.         {
  653.             //Count the number of escape characters before the index so we have a valid index into the mask
  654.             int numEscapes = 0;
  655.             for(int i = 0; i < index; ++i)
  656.             {
  657.                 if(isEscape(i))
  658.                     numEscapes++;
  659.             }
  660.  
  661.             char maskChar = mask.charAt(index + numEscapes);
  662.  
  663.             boolean isValidChar = validChar("" + targetChar, 0, maskChar) != -1 ? true : false;
  664.  
  665.             if(isValidChar)
  666.             {
  667.                 targetChar =  filterChar(targetChar, maskChar);
  668.                 return (String.valueOf(targetChar));
  669.             }
  670.             else
  671.             {
  672.                 return "";
  673.             }
  674.         }
  675.     }
  676.  
  677.     /**
  678.      * Filters the given target character with the given mask character
  679.      * @param char targetChar the character to apply the filter to.
  680.      * @param char maskChar the character to use as a filter.  Should match
  681.      * one of the pre-defined types.
  682.      * @see #applyMask
  683.      * @see #applyMaskToChar
  684.      * @see #ALPHA_TO_UPPER
  685.      * @see #ALPHA_TO_LOWER
  686.      * @see #ALPHANUMERIC_TO_UPPER
  687.      * @see #ALPHANUMERIC_TO_LOWER
  688.      */
  689.     protected char filterChar(char targetChar, char maskChar)
  690.     {
  691.         char returnChar = targetChar;
  692.  
  693.         switch (maskChar)
  694.         {
  695.             case ALPHA_TO_UPPER:
  696.                 if (Character.isLowerCase(targetChar))
  697.                     returnChar = Character.toUpperCase(targetChar);
  698.                 break;
  699.  
  700.             case ALPHA_TO_LOWER:
  701.                 if (Character.isUpperCase(targetChar))
  702.                     returnChar = Character.toLowerCase(targetChar);
  703.                 break;
  704.  
  705.             case ALPHANUMERIC_TO_UPPER:
  706.                 if (Character.isLowerCase(targetChar))
  707.                     returnChar = Character.toUpperCase(targetChar);
  708.                 break;
  709.  
  710.             case ALPHANUMERIC_TO_LOWER:
  711.                 if (Character.isUpperCase(targetChar))
  712.                     returnChar = Character.toLowerCase(targetChar);
  713.                 break;
  714.         }
  715.         return returnChar;
  716.     }
  717.  
  718.     /**
  719.      * Strips escape characters from the given string.
  720.      * For example, a mask of "99/-9/-9" and an input string of "12-3-4"
  721.      * results in "1234".
  722.      * @param s the input string
  723.      * @return the input string stripped of mask escape characters.
  724.      */
  725.     protected String stripMask(String s)
  726.     {
  727.         int sl = s.length();
  728.         int i = 0;
  729.         String newString = "";
  730.  
  731.         while (i < maskStripEscapesLen && i < sl)
  732.         {
  733.             if (isEscape(i))
  734.                 ++i;
  735.             else
  736.                 newString += s.charAt(i++);
  737.         }
  738.  
  739.         return newString;
  740.     }
  741.  
  742.     /**
  743.      * Handles KEY_PRESS processing.
  744.      * @param int the key that was pressed
  745.      */
  746.     protected void processChar(int key)
  747.     {
  748.         int            selStart            = getSelectionStart();
  749.         int            selEnd                = getSelectionEnd();
  750.         boolean    isSelection            = (selStart != selEnd);
  751.         boolean    isBackspace            = (key == BACKSPACE);
  752.         boolean    isDelete            = (key == DEL);
  753.         String        keyString            = isBackspace || isDelete ? "" : String.valueOf((char)key);
  754.         String     currentString        = getFormattedText();
  755.         int            currentLength        = currentString.length();
  756.         String        leftOfSelection        = selStart > 0 ? currentString.substring(0, selStart) : "";
  757.         String        rightOfSelection    = selEnd < currentLength ? currentString.substring(selEnd) : "";
  758.         String        finalString            = "";
  759.  
  760.         //Set the default position of the caret to be the beginning of the selection.
  761.         caret = selStart;
  762.  
  763.         //If we have no mask to worry about
  764.         if(mask == null)
  765.         {
  766.             //If we need to erase something
  767.             if(isBackspace || isDelete)
  768.             {
  769.                 if(isSelection)
  770.                 {
  771.                     finalString = leftOfSelection + rightOfSelection;
  772.                 }
  773.                 else
  774.                 {
  775.                     if(isBackspace)
  776.                     {
  777.                         String tempLeft = "";
  778.                         int leftLength = leftOfSelection.length();
  779.  
  780.                         //If the left half has any size, chop the last character of the left half off,
  781.                         //and reduce the caret position by one.
  782.                         if(leftLength > 0)
  783.                         {
  784.                             tempLeft = leftOfSelection.substring(0, leftLength - 1);
  785.                             caret--;
  786.                         }
  787.                         finalString = tempLeft + rightOfSelection;
  788.                     }
  789.                     //we assume that if you get here, the only option is for isDelete to be true.
  790.                     else
  791.                     {
  792.                         String tempRight = "";
  793.                         int rightLength = rightOfSelection.length();
  794.                         //If the right half has any size, chop the first character of the right half off.
  795.                         if(rightLength > 0)
  796.                         {
  797.                             tempRight = rightOfSelection.substring(1, rightLength);
  798.                         }
  799.                         finalString = leftOfSelection + tempRight;
  800.                     }
  801.                 }
  802.             }
  803.             //If we need to add something
  804.             else
  805.             {
  806.                 //fbeninc cheched for \0 sent with shift.
  807.                 if(keyString.equals(String.valueOf('\0')))finalString = currentString;
  808.                 else {
  809.                     caret = leftOfSelection.length() + 1;
  810.                     finalString = leftOfSelection + keyString + rightOfSelection;
  811.                 }
  812.             }
  813.             //Since there is no formatting, the finalString is the same as the unformattedString.
  814.             unformattedCurrent = finalString;
  815.         }
  816.         //We need to wory about a mask
  817.         else
  818.         {
  819.             boolean isBeep        = false;
  820.             int i;
  821.             int maskIndex;
  822.             int unformattedLength = unformattedCurrent.length();
  823.             String newLeft;
  824.             String newRight = "";
  825.             String tempString = "";
  826.             String newPartialUnformatted = "";
  827.             String maskTemplate = getMaskTemplateString();
  828.  
  829.             //Count the number of possible non-escape characters before the start of the selection
  830.             int numCharsBeforeSelStart = countNonEscapeChars(0, selStart);
  831.  
  832.             if(isSelection)
  833.             {
  834.                 //We are typing over the selection, not deleting it.
  835.                 if(!(isBackspace || isDelete))
  836.                 {
  837.                     //Find the first non-escape character in the selection
  838.                     maskIndex = selStart;
  839.                     while(maskIndex < maskStripEscapesLen)
  840.                     {
  841.                         if(isEscape(maskIndex))
  842.                         {
  843.                             ++maskIndex;
  844.                         }
  845.                         else
  846.                             break;
  847.                     }
  848.                     //If the new character is not valid at this index, eat it, beep, and do nothing.
  849.                     if(applyMaskToChar(keyString.charAt(0), maskIndex) == "")
  850.                     {
  851.                         Toolkit.getDefaultToolkit().beep();
  852.                         return;
  853.                     }
  854.                 }
  855.  
  856.                 //Count the number of real characters in the selection
  857.                 int numCharsInSelection    = 0;
  858.                 i                        = 0;
  859.                 maskIndex                = 0;
  860.                 while(i < unformattedLength && maskIndex < maskStripEscapesLen)
  861.                 {
  862.                     if(!isEscape(maskIndex))
  863.                     {
  864.                         ++i;
  865.                         if(maskIndex >= selStart && maskIndex < selEnd)
  866.                         {
  867.                             ++numCharsInSelection;
  868.                         }
  869.                     }
  870.                     ++maskIndex;
  871.                 }
  872.  
  873.                 //If the selection contains characters we care about
  874.                 if(numCharsInSelection > 0)
  875.                 {
  876.                     //We can assume that since the selection contains real characters, all non-escaped
  877.                     //characters before the selection are real too.
  878.  
  879.                     //Remove the selected characters from the unformatted string
  880.                     String tempL    = numCharsBeforeSelStart > 0 ? unformattedCurrent.substring(0, numCharsBeforeSelStart) : "";
  881.                     int tempIndex    = numCharsBeforeSelStart + numCharsInSelection;
  882.                     String tempR    = tempIndex < unformattedLength ? unformattedCurrent.substring(tempIndex) : "";
  883.                     unformattedCurrent = tempL + tempR;
  884.                     //Recalculate unformattedLength
  885.                     unformattedLength = unformattedCurrent.length();
  886.                 }
  887.                 //The selection doesn't contain any real characters, but we need handle selections of just escape characters
  888.                 else
  889.                 {
  890.                     int numNonEscapeChars = countNonEscapeChars(selStart, selEnd);
  891.                     //The selection contains only escape characters
  892.                     if(numNonEscapeChars == 0)
  893.                     {
  894.                         Toolkit.getDefaultToolkit().beep();
  895.                         return;
  896.                     }
  897.                 }
  898.             }
  899.             //There was no selection
  900.             else
  901.             {
  902.                 if(isBackspace)
  903.                 {
  904.                     //Count the number of real characters before the selection
  905.                     int numCharsBeforeSelection    = 0;
  906.                     i                            = 0;
  907.                     maskIndex                    = 0;
  908.                     while(i < unformattedLength && maskIndex < maskStripEscapesLen)
  909.                     {
  910.                         if(!isEscape(maskIndex))
  911.                         {
  912.                             ++i;
  913.                             if(maskIndex < selStart)
  914.                             {
  915.                                 ++numCharsBeforeSelection;
  916.                             }
  917.                             else
  918.                                 break;
  919.                         }
  920.                         ++maskIndex;
  921.                     }
  922.  
  923.                     //Move the caret past any consecutive escape characters
  924.                     while(caret >= 0)
  925.                     {
  926.                         if(isEscape(caret - 1))
  927.                         {
  928.                             --caret;
  929.                         }
  930.                         else
  931.                             break;
  932.                     }
  933.                     //Move the caret back one for the deleted character
  934.                     --caret;
  935.  
  936.                     //If there is a gap between characters we care about and the selection, just move the caret
  937.                     if(numCharsBeforeSelection < numCharsBeforeSelStart)
  938.                     {
  939.                         //Set the position of the caret
  940.                         select(caret, caret);
  941.                         return;
  942.                     }
  943.                     //There is no gap, and we need to delete a character
  944.                     else
  945.                     {
  946.                         //Remove the character from the unformattedCurrent string
  947.                         //A negative index is okay, since removeCharAtIndex will return the original in that case.
  948.                         unformattedCurrent = symantec.itools.util.GeneralUtils.removeCharAtIndex(unformattedCurrent, numCharsBeforeSelection - 1);
  949.                         //Recalculate unformattedLength
  950.                         unformattedLength = unformattedCurrent.length();
  951.                     }
  952.                 }
  953.                 else if(isDelete)
  954.                 {
  955.                     //Count the number of real characters after the selection
  956.                     int numCharsAfterSelection    = 0;
  957.                     i                            = 0;
  958.                     maskIndex                    = 0;
  959.                     while(i < unformattedLength && maskIndex < maskStripEscapesLen)
  960.                     {
  961.                         if(!isEscape(maskIndex))
  962.                         {
  963.                             ++i;
  964.                             if(maskIndex >= selEnd)
  965.                             {
  966.                                 ++numCharsAfterSelection;
  967.                             }
  968.                         }
  969.                         ++maskIndex;
  970.                     }
  971.  
  972.                     //If there are real characters in front of the cursor, delete one.
  973.                     //We assume that there is no gap and start dierctly after the cursor.
  974.                     if(numCharsAfterSelection > 0)// unformattedLength - numCharsBeforeSelStart)
  975.                     {
  976.                         int tempStart = numCharsBeforeSelStart < unformattedLength ? numCharsBeforeSelStart : unformattedLength - 1;
  977.                         //Remove the character from the unformattedCurrent string
  978.                         unformattedCurrent = symantec.itools.util.GeneralUtils.removeCharAtIndex(unformattedCurrent, tempStart);
  979.                         //Recalculate unformattedLength
  980.                         unformattedLength = unformattedCurrent.length();
  981.                     }
  982.                     //There are no characters we care about on the right of the cursor, so
  983.                     //beep, and do nothing.
  984.                     else
  985.                     {
  986.                         Toolkit.getDefaultToolkit().beep();
  987.                         return;
  988.                     }
  989.                 }
  990.             }
  991.  
  992.             numCharsBeforeSelStart = countNonEscapeChars(0, caret);
  993.             //If there is a gap between the current insertion point and the last character
  994.             //calculate the index of the current characters in the template, move the caret,
  995.             //and calculate a new left.
  996.             if(unformattedLength < numCharsBeforeSelStart)
  997.             {
  998.                 i                = 0;
  999.                 maskIndex        = 0;
  1000.                 while(i < unformattedLength && maskIndex < maskStripEscapesLen)
  1001.                 {
  1002.                     if(!isEscape(maskIndex))
  1003.                     {
  1004.                         ++i;
  1005.                     }
  1006.                     ++maskIndex;
  1007.                 }
  1008.                 //If there was a backspace, we let it's code handle caret manipulation
  1009.                 if(!isBackspace)
  1010.                     caret = maskIndex;
  1011.             }
  1012.             //If there is no gap, use the start of the selection to insert with.
  1013.             else
  1014.             {
  1015.                 maskIndex    = caret;    //The index in the formatted string
  1016.             }
  1017.  
  1018.             //Get the left characters
  1019.             newLeft = maskIndex > 0 ? currentString.substring(0, maskIndex) : "";
  1020.  
  1021.             //Apply the mask to the remaining non-escape characters in the right of selection and the new char
  1022.             //fbeninc
  1023.             String tempUnformatted;
  1024.             if(keyString.equals(String.valueOf('\0')))
  1025.             {
  1026.                 tempUnformatted =unformattedCurrent.substring(numCharsBeforeSelStart <= unformattedLength ? numCharsBeforeSelStart : unformattedLength);
  1027.                 caret--;
  1028.                 }
  1029.             else
  1030.             {
  1031.                 tempUnformatted = keyString + unformattedCurrent.substring(numCharsBeforeSelStart <= unformattedLength ? numCharsBeforeSelStart : unformattedLength);
  1032.                 }
  1033.  
  1034.             //Here 'i' is the index in the unformattedCurrent string, and
  1035.             i = 0;
  1036.             while(i < tempUnformatted.length() && maskIndex < maskStripEscapesLen)
  1037.             {
  1038.                 if(isEscape(maskIndex))
  1039.                 {
  1040.                     newRight += maskTemplate.charAt(maskIndex);
  1041.                 }
  1042.                 else
  1043.                 {
  1044.                     tempString = applyMaskToChar(tempUnformatted.charAt(i), maskIndex);
  1045.  
  1046.                     //If the character was filtered out by the mask the we need to beep
  1047.                     if(tempString == "")
  1048.                     {
  1049.                         isBeep = true;
  1050.                     }
  1051.                     //If the first character was accepted and we have a new character, then move the caret forward
  1052.                     else if(i == 0 && keyString != "")
  1053.                     {
  1054.                         //Move the caret past any consecutive escape characters
  1055.                         while(caret < maskStripEscapesLen)
  1056.                         {
  1057.                             if(isEscape(caret))
  1058.                             {
  1059.                                 ++caret;
  1060.                             }
  1061.                             else
  1062.                                 break;
  1063.                         }
  1064.  
  1065.                         //Move the caret one for the character that was accepted.
  1066.                         ++caret;
  1067.  
  1068.                         //Now that the caret has moved, move the caret past any consecutive escape characters
  1069.                         while(caret < maskStripEscapesLen)
  1070.                         {
  1071.                             if(isEscape(caret))
  1072.                             {
  1073.                                 ++caret;
  1074.                             }
  1075.                             else
  1076.                                 break;
  1077.                         }
  1078.                     }
  1079.  
  1080.                     //Keep track of the new acceptable characters
  1081.                     newPartialUnformatted += tempString;
  1082.                     //Keep track of the characters to display
  1083.                     newRight += tempString;
  1084.                     //Go on to the next character
  1085.                     ++i;
  1086.                 }
  1087.                 maskIndex++;
  1088.             }
  1089.  
  1090.             //Set the final string to the left, the newly constructed right, and any remainder of the mask template
  1091.             finalString = newLeft + newRight + maskTemplate.substring(newLeft.length() + newRight.length());
  1092.             //Set the unformattedCurrent to the unchanged left unformatted string and new right unformatted string.
  1093.             unformattedCurrent = unformattedCurrent.substring(0, numCharsBeforeSelStart <= unformattedLength ? numCharsBeforeSelStart : unformattedLength) + newPartialUnformatted;
  1094.             //Don't need to recalculate unformattedLength because it's not used after this
  1095.  
  1096.             if(isBeep)
  1097.                 Toolkit.getDefaultToolkit().beep();
  1098.         }
  1099.  
  1100.         //Set the field's contents to our finalString
  1101.         super.setText(finalString);
  1102.         //Set the position of the caret
  1103.         select(caret, caret);
  1104.     }
  1105.  
  1106.     /**
  1107.      * Counts the possible number of non-escape characters before the given index.
  1108.      * @param int start the index in the mask template to start from. Inclusive.
  1109.      * @param int beforeIndex the index in the mask template to count back from. Exclusive.
  1110.      * @return int the number of possible non-escape characters allowed before the given index.
  1111.      */
  1112.     protected int countNonEscapeChars(int start, int beforeIndex)
  1113.     {
  1114.         int numChars = 0;
  1115.         for(int i = start; i < beforeIndex; ++i)
  1116.         {
  1117.             if(!isEscape(i))
  1118.                 numChars++;
  1119.         }
  1120.         return numChars;
  1121.     }
  1122.  
  1123.     private boolean isEscape(int index)
  1124.     {
  1125.         return (index < 0 || index >= maskStripEscapesLen) ? false : (maskStripEscapes.charAt(index) == ESCAPE);
  1126.     }
  1127.  
  1128.     private String stripMaskEscapes()
  1129.     {
  1130.         if(mask == null)
  1131.         {
  1132.             return "";
  1133.         }
  1134.         else
  1135.         {
  1136.             int ml = mask.length();
  1137.             int maskIndex = 0;
  1138.             String nm = "";
  1139.  
  1140.             while (maskIndex < ml)
  1141.             {
  1142.                 char maskChar = mask.charAt(maskIndex++);
  1143.                 if (maskChar == ESCAPE)
  1144.                     ++maskIndex;
  1145.                 nm += maskChar;
  1146.             }
  1147.             return nm;
  1148.         }
  1149.  
  1150.     }
  1151.  
  1152.     /**
  1153.      * The string passed to setText at design time.  Returned from getText at design time.
  1154.      */
  1155.     protected String designTimeText;
  1156.     /**
  1157.      * Character constant, the backspace character.
  1158.      */
  1159.     protected final int BACKSPACE           = 8;
  1160.     /**
  1161.      * Character constant, the enter character.
  1162.      */
  1163.     protected final int ENTER               = 10;
  1164.     /**
  1165.      * Character constant, the delete character.
  1166.      */
  1167.     protected final int DEL                 = 127;
  1168.     /**
  1169.      * The zero-relative index of the character after the insertion point caret.
  1170.      */
  1171.     protected int caret;
  1172.     /**
  1173.      * The valid string format mask used to define legal field input.
  1174.      */
  1175.     protected String mask        = null;
  1176.     /**
  1177.      * The valid characters currently in the textfield, with no formatting.
  1178.      */
  1179.     protected String unformattedCurrent;
  1180.     /**
  1181.      * A string containing all the mask string characters except the ones immediately after ESCAPE characters.
  1182.      */
  1183.     protected String maskStripEscapes;
  1184.     /**
  1185.      * The length of maskStripEscapes.
  1186.      */
  1187.     protected int maskStripEscapesLen;
  1188.  
  1189.     private Key keyListener = null;
  1190. }
  1191.  
  1192.